﻿#pragma once

#include "ff_utils.h"

#include "glad.h"
#include "GLFW/glfw3.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "assimp/Importer.hpp"
#include "assimp/scene.h"
#include "assimp/postprocess.h"
#include <map>


/*
 * NOTE:
 *      for now, we only use glfw to manage window
 *      should NOT use utils_window to pass in the context
 */

namespace feifei
{
    // ------------------------------------------------------------------------
    typedef struct VertexType
    {
        glm::vec3 Position;
        glm::vec3 Normal;
        glm::vec2 TexCoord;

        glm::vec3 Tangent;  // tangent    
        glm::vec3 Bitangent;// bitangent
    }Vertex;

    typedef struct TextureType
    {
        unsigned int id;
        std::string uniform_name;
        std::string path;
        bool is_skybox;
    }Texture;

    typedef struct MaterialType
    {
        glm::vec3 ambient;   // 在环境光照下这个物体反射得是什么颜色, 通常这是和物体颜色相同的颜色
        glm::vec3 diffuse;   // 在漫反射光照下物体的颜色, 漫反射颜色也要设置为我们需要的物体颜色
        glm::vec3 specular;  // 镜面光照对物体的颜色影响（或者甚至可能反射一个物体特定的镜面高光颜色）
        float shininess;    // 高光的反光度(Shininess)。镜面高光的散射/半径
    } T_Material;

    typedef enum LightTypeEnum
    {
        DefaultLight = 0,
        DirectLight = 1,// 平行光
        PointLight = 2, // 点光源
        SpotLight = 3,  // 聚光灯
    }E_LightType;
    typedef struct LightType
    {
        E_LightType lightType;

        glm::vec3 color;
        glm::vec3 position;
        glm::vec3 direction; // 用于平行光: 从光源出发指向物体

        glm::vec3 ambient;
        glm::vec3 diffuse;
        glm::vec3 specular;

        // (点光源,聚光灯)衰减距离：http://wiki.ogre3d.org/tiki-index.php?page=-Point+Light+Attenuation
        float constant; // 常数项
        float linear;   // 一次项
        float quadratic;// 二次项

        // 聚光灯
        float cutOff;
        float outerCutOff;
    } T_Light;

    typedef struct CameraType
    {
        float radius;
        glm::vec3 position; // 摄像机位置
        glm::vec3 target;   // 摄像机目标
        glm::vec3 up;

        float fov; // 摄像机纵向张角(弧度)
        float w2h; // 宽度比高度
        float cam_near;// 到近平面距离
        float cam_far; // 到远平面距离

        glm::mat4 view; // 世界坐标系到观察空间
        glm::mat4 proj; // 观察空间到剪裁空间
    } T_Camera;

    typedef enum class MovementEnum
    {
        FORWARD,
        BACKWARD,
        LEFT,
        RIGHT,
        UP,
        DOWN
    }E_Movement;

    // ------------------------------------------------------------------------
    class Shader;
    extern void setUniBool(Shader* shader, const std::string& name, bool value);
    extern void setUniInt(Shader* shader, const std::string& name, int value);
    extern void setUniFloat(Shader* shader, const std::string& name, float value);
    extern void setUniVec2(Shader* shader, const std::string& name, const glm::vec2& value);
    extern void setUniVec2(Shader* shader, const std::string& name, float x, float y);
    extern void setUniVec3(Shader* shader, const std::string& name, const glm::vec3& value);
    extern void setUniVec3(Shader* shader, const std::string& name, float x, float y, float z);
    extern void setUniVec4(Shader* shader, const std::string& name, const glm::vec4& value);
    extern void setUniVec4(Shader* shader, const std::string& name, float x, float y, float z, float w);
    extern void setUniMat2(Shader* shader, const std::string& name, const glm::mat2& mat);
    extern void setUniMat3(Shader* shader, const std::string& name, const glm::mat3& mat);
    extern void setUniMat4(Shader* shader, const std::string& name, const glm::mat4& mat);

    // ------------------------------------------------------------------------

    extern int TextureFromFile(std::string file_name);
    extern int LoadCubemap(std::vector<std::string> tex);
    extern void BuildCoor();
    extern void BuildVectVP(std::string name, glm::vec3 vec, glm::vec3 pos, float r, float g, float b);
    extern void BuildVectPP(std::string name, glm::vec3 v_s, glm::vec3 v_e, float r, float g, float b);
    extern void BuildSkybox(std::vector<std::string> face_files);
    extern bool IsHoldCtrl();
    extern bool IsHoldShift();
    extern bool IsHoldAlt();
    
    class Shader
    {
    public:
        // constructor generates the shader on the fly
        E_ffState BuildShader(const char* vertexPath, const char* fragmentPath, const char* geometryPath = nullptr);
        E_ffState BuildShaderFromSrc(const char* vertexSrc, const char* fragmentSrc, const char* geometrySrc = nullptr);

        // activate the shader
        void Use() { glUseProgram(ID); }
        uint32_t Get() { return ID; }

    private:
        uint32_t ID;

        // utility function for checking shader compilation/linking errors.-
        void checkCompileErrors(GLuint shader, std::string type);
    };

    class Camera
    {
    public:
        void Reset(int width, int height);
        void SetTarget(glm::vec3 tar);
        void SetPosition(glm::vec3 pos);

        glm::vec3 Tar() { return target; }
        glm::vec3 Pos() { return position; }
        glm::mat4 View() { return view; }
        glm::mat4 Proj() { return projction; }

        glm::vec3 Up() { return up; }
        glm::vec3 WorldUp() { return worldUp; }

        void OnKeyboard(int key, int scancode, int action, int mods);
        void OnKeyboard2(int key, int scancode, int action, int mods);
        void OnMouseScroll(double yoffset);
        void OnMouseMove(double xpos, double ypos);
        void OnMouseButton(int button, int action, int mods);
        void OnWindowResize(int width, int height);

    private:

        glm::vec3 worldUp = glm::vec3(0.0f, 1.0f, 0.0f);
        int winWidth;
        int winHeight;
        float zoom; // 像机纵向张角(degree)
        float yaw;  // 方位角: 视角在XZ平面与X轴正向夹角(degree), 向Z轴正方向旋转
        float pitch;// 俯仰角: 视角与XZ平面夹角, 负号表示向下(degree)
        float movementSpeed; // 摄像机移动速度
        const float moveFast = 1.0f;
        const float moveNormal = 0.1f;
        const float moveSlow = 0.01f;
        float mouseSensitivity = 0.1f; // 视角移动速度

        glm::vec3 position; // 摄像机位置
        glm::vec3 target;   // 摄像机目标
        glm::vec3 up;       // 摄像机当前向上方向的向量
        glm::vec3 front;    // 摄像机当前向前方向的向量
        glm::vec3 right;    // 摄像机当前向右方向的向量
        float fov; // 摄像机纵向张角(弧度)
        float w2h; // 宽度比高度
        float cam_near = 0.1f;// 到近平面距离
        float cam_far = 100.0f; // 到远平面距离

        glm::mat4 view; // 世界坐标系到观察空间
        glm::mat4 projction; // 观察空间到剪裁空间

        void updateTarPos();
        void updateYawPitch();
        void moveCamera(E_Movement move, bool isFixTarget = false);
        void moveTarget(E_Movement move);
    };

    class Mesh
    {
    public:
        Mesh() { }
        Mesh(std::vector<Vertex> vertices);
        Mesh(std::vector<Vertex> vertices, std::vector<unsigned int> indices);
        Mesh(std::vector<Vertex> vertices, std::vector<unsigned int> indices, std::vector<Texture> textures);

        E_ffState AddVertices(float* vertex_buff, uint32_t count, bool en_normal = true, bool en_tex_coor = true);
        E_ffState AddIndices(uint32_t* index_buff, uint32_t count);
        E_ffState AddTexture(std::string file_name, std::string uniform_name);
        E_ffState AddTexture(unsigned char * data, int width, int height, std::string uniform_name);
        E_ffState AddCubeMap(std::vector<std::string> face_files, std::string uniform_name);

        Shader* GetShader() { return shader; }
        E_ffState BindShader(Shader* s);
        E_ffState BindShader(std::string name);
        E_ffState BindMaterial(T_Material* mater);

        void Draw();
        int PolygonMode = GL_TRIANGLES;

    private:
        bool enNormal = true;
        bool enTexCoor = true;
        Shader* shader = nullptr;
        T_Material* material = nullptr; // 材质参数，非材质贴图        
        std::vector<Vertex> vertices;   // vertexArray 按照 position, normal, texcoor存储; polygonMode为顶点个数
        std::vector<uint32_t> indices;
        std::vector<Texture> textures;
        unsigned int VAO = 0; // 顶点数组对象,用来管理顶点的属性
        unsigned int VBO = 0; // 顶点缓冲区对象,用来管理顶点显存
        unsigned int EBO = 0; // 索引缓冲区对象,用来管理索引显存

        void setupMesh();
        void setupMeshVertices();
        void setupMeshIndices();
    };

    class Model
    {
    public:
        Model()
        {
            AddCoor();
        }

        // model data 
        std::vector<Texture> textures_loaded;	// stores all the textures loaded so far, optimization to make sure textures aren't loaded more than once.
        std::vector<Mesh*> meshes;
        std::string directory;
        bool gammaCorrection = false;

        // draws the model, and thus all its meshes
        void Draw(Shader* s = nullptr);
        void AddCoor();
        Mesh* mesh_coor;
        bool EnShowCoor = false;
        bool IsSkybox = false;
        int DepthFunc = GL_LESS;

        // loads a model with supported ASSIMP extensions from file and stores the resulting meshes in the meshes vector.
        E_ffState LoadModel(std::string const& path)
        {
            // read file via ASSIMP
            Assimp::Importer importer;
            const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs | aiProcess_CalcTangentSpace);
            // check for errors
            if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero
            {
                ERR("ERROR::ASSIMP:: %s", importer.GetErrorString());
            }
            // retrieve the directory path of the filepath
            directory = path.substr(0, path.find_last_of('/'));

            // process ASSIMP's root node recursively
            processNode(scene->mRootNode, scene);

            return E_ffState::SUCCESS;
        }
        Mesh* CreateMesh() 
        { 
            Mesh* mesh = new Mesh();  
            meshes.push_back(mesh); 
            return mesh; 
        }
        Mesh* GetMesh(uint32_t idx)
        {
            if (idx < meshes.size())
                return meshes[idx];
            else
                return nullptr;
        }

        E_ffState BindCamera(Camera* c);
        E_ffState BindCamera(std::string name);

        void OnKeyboard(int key, int scancode, int action, int mods);
        void OnMouseScroll(double yoffset);
        void OnMouseMove(double xpos, double ypos);
        void OnMouseButton(int button, int action, int mods);

        void Reset()
        {
            modelScale = glm::vec3(1.0f); // 对角阵做缩放矩阵
            modelPos = glm::vec3(0.0);
            modelRotate = glm::vec3(0.0);
            modelMatrix = glm::mat4(1.0f);
        }
        
        void RotateInc(float degree, glm::vec3 v)
        {
            modelRotate = modelRotate + degree * v;
        }
        void MoveAbs(glm::vec3 m) { modelPos = m; }             void MoveInc(glm::vec3 m) { modelPos = modelPos + m; }
        void ScaleAbs(float s) { modelScale = glm::vec3(s); }   void Scale(float s) { modelScale = s * modelScale; }
        glm::vec3 ModelScale() { return modelScale; }           void ModelScale(glm::vec3 s) { modelScale = s; }
        glm::vec3 ModelPos() { return modelPos; }               void ModelPos(glm::vec3 p) { modelPos = p; }
        glm::mat4 ModelMatrix() { return modelMatrix; }         void ModelMatrix(glm::mat4 m) { modelMatrix = m; }
        bool UpdateModelMatrixPerDraw = true;

    private:
        // processes a node in a recursive fashion. Processes each individual mesh located at the node and repeats this process on its children nodes (if any).
        void processNode(aiNode* node, const aiScene* scene)
        {
            // process each mesh located at the current node
            for (unsigned int i = 0; i < node->mNumMeshes; i++)
            {
                // the node object only contains indices to index the actual objects in the scene. 
                // the scene contains all the data, node is just to keep stuff organized (like relations between nodes).
                aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
                meshes.push_back(processMesh(mesh, scene));
            }
            // after we've processed all of the meshes (if any) we then recursively process each of the children nodes
            for (unsigned int i = 0; i < node->mNumChildren; i++)
            {
                processNode(node->mChildren[i], scene);
            }
        }

        // create a new mesh from file: mesh vertices, mesh index, and mesh textures
        Mesh *processMesh(aiMesh* mesh, const aiScene* scene)
        {
            // data to fill
            std::vector<Vertex> vertices;
            std::vector<unsigned int> indices;
            std::vector<Texture> textures;

            // walk through each of the mesh's vertices
            for (unsigned int i = 0; i < mesh->mNumVertices; i++)
            {
                Vertex vertex;
                glm::vec3 vector; // we declare a placeholder vector since assimp uses its own vector class that doesn't directly convert to glm's vec3 class so we transfer the data to this placeholder glm::vec3 first.
                // positions
                vector.x = mesh->mVertices[i].x;
                vector.y = mesh->mVertices[i].y;
                vector.z = mesh->mVertices[i].z;
                vertex.Position = vector;
                // normals
                if (mesh->HasNormals())
                {
                    vector.x = mesh->mNormals[i].x;
                    vector.y = mesh->mNormals[i].y;
                    vector.z = mesh->mNormals[i].z;
                    vertex.Normal = vector;
                }
                // texture coordinates
                if (mesh->mTextureCoords[0]) // does the mesh contain texture coordinates?
                {
                    glm::vec2 vec;
                    // a vertex can contain up to 8 different texture coordinates. We thus make the assumption that we won't 
                    // use models where a vertex can have multiple texture coordinates so we always take the first set (0).
                    vec.x = mesh->mTextureCoords[0][i].x;
                    vec.y = mesh->mTextureCoords[0][i].y;
                    vertex.TexCoord = vec;
                    // tangent
                    vector.x = mesh->mTangents[i].x;
                    vector.y = mesh->mTangents[i].y;
                    vector.z = mesh->mTangents[i].z;
                    vertex.Tangent = vector;
                    // bitangent
                    vector.x = mesh->mBitangents[i].x;
                    vector.y = mesh->mBitangents[i].y;
                    vector.z = mesh->mBitangents[i].z;
                    vertex.Bitangent = vector;
                }
                else
                {
                    vertex.TexCoord = glm::vec2(0.0f, 0.0f);
                }
                vertices.push_back(vertex);
            }
            // now wak through each of the mesh's faces (a face is a mesh its triangle) and retrieve the corresponding vertex indices.
            for (unsigned int i = 0; i < mesh->mNumFaces; i++)
            {
                aiFace face = mesh->mFaces[i];
                // retrieve all indices of the face and store them in the indices vector
                for (unsigned int j = 0; j < face.mNumIndices; j++)
                {
                    indices.push_back(face.mIndices[j]);
                }
            }
            // process materials
            aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
            // we assume a convention for sampler names in the shaders. Each diffuse texture should be named
            // as 'texture_diffuseN' where N is a sequential number ranging from 1 to MAX_SAMPLER_NUMBER. 
            // Same applies to other texture as the following list summarizes:
            // diffuse: texture_diffuseN
            // specular: texture_specularN
            // normal: texture_normalN

            // 1. diffuse maps
            std::vector<Texture> diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, "material.diffuse");
            textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
            // 2. specular maps
            std::vector<Texture> specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "material.specular");
            textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
            // 3. normal maps
            std::vector<Texture> normalMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, "material.normal");
            textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());
            // 4. height maps
            std::vector<Texture> heightMaps = loadMaterialTextures(material, aiTextureType_AMBIENT, "material.height");
            textures.insert(textures.end(), heightMaps.begin(), heightMaps.end());

            // return a mesh object created from the extracted mesh data
            return new Mesh(vertices, indices, textures);
        }

        // checks all material textures of a given type and loads the textures if they're not loaded yet.
        // the required info is returned as a Texture struct.
        std::vector<Texture> loadMaterialTextures(aiMaterial* mat, aiTextureType type, std::string uniformName)
        {
            std::vector<Texture> textures;
            for (unsigned int i = 0; i < mat->GetTextureCount(type); i++)
            {
                aiString filename;
                mat->GetTexture(type, i, &filename);
                // check if texture was loaded before and if so, continue to next iteration: skip loading a new texture
                bool skip = false;
                for (unsigned int j = 0; j < textures_loaded.size(); j++)
                {
                    if (std::strcmp(textures_loaded[j].path.data(), filename.C_Str()) == 0)
                    {
                        textures.push_back(textures_loaded[j]);
                        skip = true; // a texture with the same filepath has already been loaded, continue to next one. (optimization)
                        break;
                    }
                }
                if (!skip)
                {   // if texture hasn't been loaded already, load it
                    Texture texture;

                    texture.id = TextureFromFile(filename.C_Str());
                    texture.path = filename.C_Str();
                    texture.uniform_name = uniformName;
                    textures.push_back(texture);
                    textures_loaded.push_back(texture);  // store it as texture loaded for entire model, to ensure we won't unnecesery load duplicate textures.
                }
            }
            return textures;
        }

        // we assume all meshes in a model share the transform and shader
        glm::vec3 modelScale = glm::vec3(1.0f);
        glm::vec3 modelPos = glm::vec3(0.0); // 模型位置
        glm::vec3 modelRotate = glm::vec3(0.0);
        glm::mat4 modelMatrix = glm::mat4(1.0f);
        float movementSpeed = 1.0f; // 移动速度

        void updateModelMatrix()
        {
            modelMatrix = glm::mat4(1.0f);
            modelMatrix = glm::translate(modelMatrix, modelPos);
            modelMatrix = glm::scale(modelMatrix, modelScale);
            modelMatrix = glm::rotate(modelMatrix, modelRotate.x, glm::vec3(1.0, 0.0, 0.0));
            modelMatrix = glm::rotate(modelMatrix, modelRotate.y, glm::vec3(0.0, 1.0, 0.0));
            modelMatrix = glm::rotate(modelMatrix, modelRotate.z, glm::vec3(0.0, 0.0, 1.0));
        }

        Camera* camera = nullptr;
    };

    class GlfwWindow
    {
    public:
        E_ffState CreateGlfwWindow(std::string title, int width, int height);
        GLFWwindow* GetGlfwWindow() { return window; }

        int Width;
        int Height;
        std::string Title;

    private:
        GLFWwindow* window;
        GLFWcursorposfun CursorPosFunc;
        GLFWscrollfun ScrollFunc;
    };

    // ------------------------------------------------------------------------
    typedef void(*ResizeFBHandle)(int, int);
    typedef void(*ScrollHandle)(double, double);
    typedef void(*CursorPosHandle)(double, double);
    typedef void(*UpdatePerDrawHandle)();
    typedef void(*MouseButtonHandle)(int, int, int);
    typedef void(*KeyBoardHandle)(int, int, int, int);
    class OGlHandle
    {
    public:
        static OGlHandle* GetOglHandle();

        E_ffState Init();
        void Release();
        void Run();

        std::vector<ResizeFBHandle> ResizeFBCallbacks;
        std::vector<ScrollHandle> ScrollCallbacks;
        std::vector<CursorPosHandle> CursorPosCallbacks;
        std::vector<MouseButtonHandle> MouseButtonCallbacks;
        std::vector<KeyBoardHandle> KeyBoardCallbacks;
        std::vector<UpdatePerDrawHandle> UpdatePerDraws;
        void AddResizeFBCallback(ResizeFBHandle f) { ResizeFBCallbacks.push_back(f); }
        void AddScrollCallback(ScrollHandle f) { ScrollCallbacks.push_back(f); }
        void AddCursorPosCallback(CursorPosHandle f) { CursorPosCallbacks.push_back(f); }
        void AddMouseButtonCallback(MouseButtonHandle f) { MouseButtonCallbacks.push_back(f); }
        void AddKeyBoardCallback(KeyBoardHandle f) { KeyBoardCallbacks.push_back(f); }
        void AddUpdatePerDrawCallback(UpdatePerDrawHandle f) { UpdatePerDraws.push_back(f); }

        GlfwWindow window;
        Model* CurrModel;
        Shader* CurrShader;
        Camera* CurrCamera;
        T_Light* CurrLight;
        T_Material* CurrMaterial;
        std::map<std::string, Model*> models;
        std::map<std::string, Shader*> shaders;
        std::map<std::string, Camera*> cameras;
        std::map<std::string, T_Light> lights;
        std::map<std::string, T_Material> materials;
        
        E_ffState AddModel(std::string name, Model* model)
        {
            std::pair<std::map<std::string, Model*>::iterator, bool> rtn;
            rtn = models.insert(std::pair<std::string, Model*>(name, model));
            if (rtn.second == true)
                return E_ffState::SUCCESS;
            else
                return E_ffState::RTN_ERR;
        }
        E_ffState AddShader(std::string name, Shader* shader)
        {
            std::pair<std::map<std::string, Shader*>::iterator, bool> rtn;
            rtn = shaders.insert(std::pair<std::string, Shader*>(name, shader));
            if (rtn.second == true)
                return E_ffState::SUCCESS;
            else
                return E_ffState::RTN_ERR;
        }
        E_ffState AddCamera(std::string name, Camera* camera)
        {
            std::pair<std::map<std::string, Camera*>::iterator, bool> rtn;
            rtn = cameras.insert(std::pair<std::string, Camera*>(name, camera));
            if (rtn.second == true)
                return E_ffState::SUCCESS;
            else
                return E_ffState::RTN_ERR;
        }
        E_ffState AddLight(std::string name, T_Light light)
        {
            std::pair<std::map<std::string, T_Light>::iterator, bool> rtn;
            rtn = lights.insert(std::pair<std::string, T_Light>(name, light));
            if (rtn.second == true)
                return E_ffState::SUCCESS;
            else
                return E_ffState::RTN_ERR;
        }
        E_ffState AddMaterial(std::string name, T_Material mater)
        {
            std::pair<std::map<std::string, T_Material>::iterator, bool> rtn;
            rtn = materials.insert(std::pair<std::string, T_Material>(name, mater));
            if (rtn.second == true)
                return E_ffState::SUCCESS;
            else
                return E_ffState::RTN_ERR;
        }
        Model* GetModel(std::string name)
        {
            std::map<std::string, Model*>::iterator iter;
            iter = models.find(name);

            if (iter != models.end())
                return models[name];
            else
                return nullptr;
        }
        Shader* GetShader(std::string name)
        {
            std::map<std::string, Shader*>::iterator iter;
            iter = shaders.find(name);

            if (iter != shaders.end())
                return shaders[name];
            else
                return nullptr;
        }
        Camera* GetCamera(std::string name)
        {
            std::map<std::string, Camera*>::iterator iter;
            iter = cameras.find(name);

            if (iter != cameras.end())
                return cameras[name];
            else
                return nullptr;
        }
        T_Light* GetLight(std::string name)
        {
            std::map<std::string, T_Light>::iterator iter;
            iter = lights.find(name);

            if (iter != lights.end())
                return &lights[name];
            return nullptr;
        }
        T_Material * GetMaterial(std::string name)
        {
            std::map<std::string, T_Material>::iterator iter;
            iter = materials.find(name);

            if (iter != materials.end())
                return &materials[name];
            return nullptr;
        }
        E_ffState SetModel(std::string name)
        {
            Model* model = GetModel(name);
            if (model != nullptr)
            {
                CurrModel = model;
                return E_ffState::SUCCESS;
            }
            else
            {
                return E_ffState::RTN_ERR;
            }
        }
        E_ffState SetShader(std::string name)
        {
            Shader* shader = GetShader(name);
            if (shader != nullptr)
            {
                CurrShader = shader;
                return E_ffState::SUCCESS;
            }
            else
            {
                return E_ffState::RTN_ERR;
            }
        }
        E_ffState SetCamera(std::string name)
        {
            Camera* camera = GetCamera(name);
            if (camera != nullptr)
            {
                CurrCamera = camera;
                return E_ffState::SUCCESS;
            }
            else
            {
                return E_ffState::RTN_ERR;
            }
        }
        E_ffState SetLight(std::string name)
        {
            T_Light* light = GetLight(name);
            if (light != nullptr)
            {
                CurrLight = light;
                return E_ffState::SUCCESS;
            }
            else
            {
                return E_ffState::RTN_ERR;
            }
        }
        E_ffState SetMaterial(std::string name)
        {
            T_Material* material = GetMaterial(name);
            if (material != nullptr)
            {
                CurrMaterial = material;
                return E_ffState::SUCCESS;
            }
            else
            {
                return E_ffState::RTN_ERR;
            }
        }

        float TimeValue = 0; // seconds
        bool isQuite = false;
        bool isPause = false;
        Event EvtContinue;
        Thread TidDraw;

    private:
        OGlHandle() { EvtContinue = EventCreate(); };
        static OGlHandle* gOGLHandleSingleton;
    };

} // end namespace feifei
